#include "reader.h"
#include "systick.h"
#include "rfid_rc522.h"
#include "display.h"
#include "pos_param.h"
#include "reader_cpu.h"
#include "XRCORE.h"
#include <string.h>
#include "pos.h"
#include "RTClock.h"
#include "beep.h"
#include "pos_timer.h"


#define DefaultBlock  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }

volatile TReader Reader;

volatile uint8_t  GCardOn = false;
volatile uint8_t  GOperOn = false;
volatile uint8_t GNumBlock[16] = DefaultBlock;
volatile uint8_t GNumBlockTem[16];
volatile uint16_t LimitMoneyBuf;
volatile uint8_t  LimitMoneyDate;
volatile uint8_t GCPUCard = 0; // 1:CPU卡； 0:M1卡
// extern uint8_t GDataType;

uint16_t oType;
uint8_t G_Error_Count;

const uint8_t M1AuthModeKeyA = 0x60;

static uint16_t DoAuthM1 ( uint8_t * ACardSN )
{
    return PcdAuthState ( M1AuthModeKeyA, GetCardArea ( PosParam.M1Purse.Sector, 3 ), (unsigned char *)PosParam.M1Purse.Key, ACardSN );
    
}

// GetCardArea 获取M1卡的块号
uint8_t GetCardArea ( uint8_t sector, uint8_t block )
{
    return 4 * sector + block; 
}

// 1. 寻卡，获取卡类型
static uint16_t FindCard ( uint8_t * CardSN )
{
    static uint8_t oTypeBuf[2] = {0, 0};
    static uint8_t oCode;
    static uint8_t err;
	static uint8_t CardSak;
    uint32_t CardType_Buf;
    static unsigned char recive_data[2];

#ifdef DUAL_MODE
    if(GCPUCard==true && CheckCpuCard()==0){  //CPU?????????RC522, ??RC522???CPU????????? ?????CPU?????
        return 0;
    } else if(CheckM1Card()==0) {
        return 0;
    }
#endif
    if (PcdRequest(PICC_REQALL, oTypeBuf) != MI_OK) 
    {
        return 1;
    }
    delay_ms(100);
    
    if ( PcdRequest(PICC_REQALL, oTypeBuf ) != MI_OK ) 
    {        //寻卡失败
        if ( PcdRequest(PICC_REQALL, oTypeBuf ) != MI_OK )
        {     //
            PcdReset();    
            return 1;
        }
	}
 
    oType = ( oTypeBuf[0] << 8 ) + oTypeBuf[1]; //组合oTypeBuf[0]和oTypeBuf[1]为卡类型,oTypeBuf[0]是高位，oTypeBuf[1]是低位
    Reader.Purse.CardType = oType;

    if ( PcdAnticoll(CardSN) != MI_OK ) //获取卡片UID：卡片的物理卡号
    {  
        return 302;
	}

    err = PcdSelect(CardSN, &CardSak);    // 获取卡片的SAK值
    if(err != MI_OK)
    {   
        return 303;
    }
 
    if ( CardSN[0] == 0xAC && CardSN[1] == 0x95 && CardSN[2] == 0x64 && CardSN[3] == 0x6A )  //检测到UID是这个卡号  恢复出厂设置，程序初始化卡
    {
        disp.Clear();
        Beep.BeepOnce();
        delay_ms ( 500 );
        SetFactory();
        disp.Complete();
        Beep.BeepTime ( 1200 );
        disp.Clear();
        return 1;
    }

    if ( PosParam.Prev_UID == 1 && GCPUCard == false) //判断卡片是否为UID卡
    {
        Reader.HaltCard();
        SetBitMask(BitFramingReg, 0x07);
        recive_data[0] = 0x40;
        PcdComMF522(PCD_TRANSCEIVE, recive_data, 1, recive_data, &CardType_Buf);

        if ((recive_data[0] & 0xf) == 0xA)
        {
            PcdReset();
            disp.Clear();
            Beep.BeepGap(3);
#ifdef SEGLCD    
            disp.LcdData ( 10, 0x0D );  //U
            disp.LcdData ( 9, 0x06 );
            disp.LcdData ( 17, 0x08 );
            disp.OneNum ( 2, 0, 1 );
            disp.OneNum ( 3, 0, 'd' );
        #elif defined DIGLED
            disp.OneNum(2, 0, 'E'); 
            disp.OneNum(3, 0, '-'); 
            disp.OneNum(4, 0, 'U'); 
            disp.OneNum(5, 0, '1'); 
            disp.OneNum(6, 0, 'D'); 
        #endif

            delay_ms(1000);

            disp.Clear();
            return 1;
        }
        if (oType == 0x0400) 
        { // MI 
            PcdReset();
        }
        PcdRequest(PICC_REQALL, oTypeBuf);
        PcdAnticoll(CardSN);
        PcdSelect(CardSN, &CardSak);

    }

    if (oType == 0x0400)// 判断卡片类型为M1卡；
    { 
       // if(CardSak != 0x28 )
          //{
            GCPUCard = false;
            return 0;
         // }
    }
    GCPUCard = true;
    // RATS
    if (CardReset(CPUReadBuf, oTypeBuf) != MI_OK)
    {
        return 304;
    }

    if (Pcd_Cmd(CmdSelectFile, 7, CPUReadBuf, oTypeBuf) != MI_OK) 
    {
        if (Pcd_Cmd(CmdSelectFile, 7, CPUReadBuf, oTypeBuf) != MI_OK) 
        {
            return 305;
        }
        return 0;
    }
    if ((CPUReadBuf[oTypeBuf[0]] << 8 | CPUReadBuf[oTypeBuf[0] + 1]) != 0x9000) 
    {
        return 305;
    }

    return 0;
}

// ReadNum 读卡号
static uint16_t ReadNum(uint8_t *ACardSN)
{
    static uint16_t err, count;
    static uint8_t i;
    // M1卡验证密钥； CPU读取文件;
    if (GCPUCard) //判断卡片是否为CPU卡以及是否禁用CPU卡
    {
        err = DoAuthCPU(ACardSN);  //CPU卡验证
        if (err != nil) 
        {
            return err;
        }
    }
    else {
 
        err = DoAuthM1(ACardSN); //M1卡验证
        if (err != nil) 
        {
            return 401;
        }
    }

 
    if ( GCPUCard)
    {
        err = CmdReadCPUFile(0x86, (uint8_t *)&GNumBlock, 0x10);
    }else{
        err = PcdRead(GetCardArea(PosParam.M1Purse.Sector, 1), (uint8_t *)&GNumBlock);
    }
    if (err != nil) 
    {
        return 501;
    }

    // ???????
    count = 0;
    for (i=0 ; i<16 ; i++)
    {
        count = count + GNumBlock[i];
    }
    if (count == 0)
    {
        return 888;
    }

    err = DecodeNumBlock(ACardSN, (uint8_t *)&GNumBlock, PosParam.M1Purse.DataType, GCPUCard); // 1?????
    if ( err != nil ) {
       return 502;
    }

    memmove((void *)Reader.Purse.EmbedSN, ACardSN, 4);
    memmove((void *)&Reader.Purse.AuthCode, (void *)&GNumBlock[2], 2);                                         // ?????
    Reader.Purse.AuthCode = ((Reader.Purse.AuthCode & 0x00FF) << 8) | ((Reader.Purse.AuthCode & 0xFF00) >> 8); // ?????
    GOperOn = ( GNumBlock[5] != 0xBB );   //?????????

    return 0;
}

// ReadMoney 
static uint16_t ReadMoney()
{
    uint16_t err, err2;
    static uint8_t oBlock0[16] = DefaultBlock, oBlock2[16] = DefaultBlock;
    uint8_t oDateuse0, oDateuse2, oDate0, oDate2, oMonth0, oMonth2;
    uint16_t oMonthuse0, oMonthuse2, oDayLimitAmo0, oDayLimitAmo2;
    uint16_t oSerial0, oSerial2;
    uint32_t oBalance0, oBalance2;

    // ????? 0 ? 2
    if (GCPUCard && PosParam.EnableUserCard!=0x02) 
    {
        uint8_t cmd1 = PosParam.CPU_B ? 0x8B : 0x88;
        uint8_t cmd2 = PosParam.CPU_B ? 0x8A : 0x87;
        err = CmdReadCPUFile(cmd1, (uint8_t *)&oBlock0, 0x10);
        err2 = CmdReadCPUFile(cmd2, (uint8_t *)&oBlock2, 0x10);
    } else 
    {
        err = PcdRead(GetCardArea(PosParam.M1Purse.Sector, 0), (uint8_t *)&oBlock0);
        err2 = PcdRead(GetCardArea(PosParam.M1Purse.Sector, 2), (uint8_t *)&oBlock2);
    }

    if (err != nil || err2 != nil) 
    {
        if (GCPUCard) 
        {
            PcdReset();
        }
        if (err != nil) {
            return 503;
        } else {
            return 504;
        }
    }

    // ????
    err = DecodeMoneyBlockAll((uint8_t *)Reader.Purse.EmbedSN, (uint8_t *)&oBlock0, &oBalance0, &oSerial0, &oDate0, 
                        &oDateuse0, &oMonth0, &oMonthuse0, &oDayLimitAmo0, PosParam.M1Purse.DataType, GCPUCard);
    err2 = DecodeMoneyBlockAll((uint8_t *)Reader.Purse.EmbedSN, (uint8_t *)&oBlock2, &oBalance2, &oSerial2, &oDate2, 
                        &oDateuse2, &oMonth2, &oMonthuse2, &oDayLimitAmo2, PosParam.M1Purse.DataType, GCPUCard);
    // ????
    if (err != nil && err2 != nil) {
        return 505;
    }

    if (err != nil || (err == nil && err2 == nil && oBalance0 > oBalance2)) {
        Reader.Purse.DateUsage = oDateuse2;
        Reader.Purse.Date = oDate2;
        Reader.Purse.TradeSerial = oSerial2;
        Reader.Purse.Balance = oBalance2;
        Reader.Purse.DayLimitAmo = oDayLimitAmo2;
        Reader.Purse.Month = oMonth2;
        Reader.Purse.MonthUsage = oMonthuse2;
    } else {
        Reader.Purse.DateUsage = oDateuse0;
        Reader.Purse.Date = oDate0;
        Reader.Purse.TradeSerial = oSerial0;
        Reader.Purse.Balance = oBalance0;
        Reader.Purse.DayLimitAmo = oDayLimitAmo0;
        Reader.Purse.Month = oMonth0;
        Reader.Purse.MonthUsage = oMonthuse0;
    }

    return 0;
}

// PrepaySmall ????
uint16_t PrepaySmall (uint16_t Amount, uint8_t IsFirst)
{
    static uint32_t oBalance;
    static uint8_t oBlock[16] = DefaultBlock;
    static uint16_t err,err2;

    if (Reader.Purse.Balance < (uint32_t)Amount) {
        // Valve_OFF;
        disp.Balance (Reader.Purse.Balance );
    #ifdef SEGLCD
        disp.LcdData (17, 0x02); // ?
        disp.LcdData (11, 0x08); // ?
        //disp.LcdData (17, 0x08); // ??
    #endif
        // Beep.BeepGap(3);
        // CPU_M1_While_CardOn();
        // while(While_CardOn() == nil);
        return 1;
    }

    oBalance = Reader.Purse.Balance - (uint32_t)Amount;
    //????? ????
    if(PosParam.DayLimitFlag == 1) {
        GetClock();
        if(c.Day != Reader.Purse.Date) {
            Reader.Purse.Date = c.Day;
            Reader.Purse.DateUsage = 0;
            Reader.Purse.DayLimitAmo = 0;
        }
        if(Reader.Purse.DayLimitAmo < PosParam.DayLimit) {   
            Reader.Purse.DayLimitAmo = Reader.Purse.DayLimitAmo + Amount;
        }   
    }

    if (IsFirst != 0) {
        Reader.Purse.TradeSerial++;
    }

    EncodeMoneyBlockAll ((uint8_t *)Reader.Purse.EmbedSN,  
                         oBlock,  
                         &oBalance, 
                        (uint16_t *)&Reader.Purse.TradeSerial,
                        (uint8_t *)&Reader.Purse.Date, 
                        (uint8_t *)&Reader.Purse.DateUsage, 
                        (uint8_t *)&Reader.Purse.Month,
                        (uint16_t *)&Reader.Purse.MonthUsage, 
                        (uint16_t *)&Reader.Purse.DayLimitAmo,
						PosParam.M1Purse.DataType, GCPUCard);
    if (GCPUCard) {
        uint8_t cmd1 = PosParam.CPU_B ? 0x8B : 0x88;
        uint8_t cmd2 = PosParam.CPU_B ? 0x8A : 0x87;
        err = CmdWriteCPUBlock(cmd1, oBlock);
        if (err != nil) {
            return 506;
        }
        err = CmdWriteCPUBlock(cmd2, oBlock);
        if (err != nil) {
            return 507;
        }
    } else {
        err = PcdWrite(GetCardArea(PosParam.M1Purse.Sector, 0), oBlock);
        if (err != nil) {
            return 506;
        }
        err = PcdWrite(GetCardArea(PosParam.M1Purse.Sector, 2), oBlock);
        if (err != nil) {
            return 507;
        }
    }

    Reader.Purse.Balance = oBalance;
    PosParam.Amtall = PosParam.Amtall + (uint32_t) Amount;
    return 0;
}


// ???? ????
uint16_t FreePaySmall(uint16_t Amount, uint8_t IsFirst)
{
    static uint8_t oBalance;
    static uint8_t oBlock[16] = DefaultBlock;
    static uint16_t err = 0;
    static uint16_t oBalance2;

    if ( IsFirst != 0 ){
        Reader.Purse.TradeSerial++;
    }

    GetClock();
    if(PosParam.FreeUseMonFlag != 1){
        if(c.Day != Reader.Purse.Date){
            Reader.Purse.Date = c.Day;
            Reader.Purse.DateUsage = 0;
            Reader.Purse.DayLimitAmo = 0;
        }
        if (PosParam.FreeUseTotal - Reader.Purse.DateUsage < (uint32_t)Amount) {
        #ifdef SEGLCD
            disp.LcdData (17, 0x02 + 0x08); // ?
            disp.WriteMulDig(6, 2, PosParam.FreeUseTotal - Reader.Purse.MonthUsage);
            disp.LcdData (11, 0x02 + 0x04); // ???? + ?
        #elif defined DIGLED 
            disp.ShowString("  FULL  ");
        #endif
            Valve_OFF;
            return 1;
        }

        if((c.Day == Reader.Purse.Date) && (Reader.Purse.DateUsage >= PosParam.FreeUseTotal)){
           return 1;
        }else{   
            oBalance = Reader.Purse.DateUsage + (uint32_t)Amount; 
        }

        EncodeMoneyBlockAll ((uint8_t *)Reader.Purse.EmbedSN,   
                             oBlock,  
                            (uint32_t *)&Reader.Purse.Balance,
                            (uint16_t *)&Reader.Purse.TradeSerial,   
                            (uint8_t *)&Reader.Purse.Date,    
                            &oBalance,                              //????
                            (uint8_t *)&Reader.Purse.Month,    
                            (uint16_t *)&Reader.Purse.MonthUsage, 
                            (uint16_t *)&Reader.Purse.DayLimitAmo,
							PosParam.M1Purse.DataType, GCPUCard);
    }else{     
        if(c.Month != Reader.Purse.Month){
            Reader.Purse.Month = c.Month;
            Reader.Purse.MonthUsage = 0;
        }
        if (PosParam.FreeUseTotal - Reader.Purse.MonthUsage < (uint32_t)Amount) {
        #ifdef SEGLCD
            disp.LcdData (17, 0x02 + 0x08); // ?
            disp.WriteMulDig(6, 2, PosParam.FreeUseTotal - Reader.Purse.MonthUsage);
            disp.LcdData (11, 0x02 + 0x04); // ???? + ?
        #elif defined DIGLED 
            disp.ShowString("  FULL  ");
        #endif
            return 1;
        }

        if((c.Month == Reader.Purse.Month) && (Reader.Purse.MonthUsage >= PosParam.FreeUseTotal)){   
            return 1; 
        }else {    
            oBalance2 = Reader.Purse.MonthUsage + (uint32_t)Amount; 
        }

        EncodeMoneyBlockAll ((uint8_t *)Reader.Purse.EmbedSN,    
                             oBlock,   
                            (uint32_t *)&Reader.Purse.Balance,
                            (uint16_t *)&Reader.Purse.TradeSerial,   
                            (uint8_t *)&Reader.Purse.Date,
                            (uint8_t *)&Reader.Purse.DateUsage,    
                            (uint8_t *)&Reader.Purse.Month,
                            &oBalance2,                             //????
                            (uint16_t *)&Reader.Purse.DayLimitAmo,
							PosParam.M1Purse.DataType, GCPUCard);
    }

    if (GCPUCard) {
        if(PosParam.CPU_B) {   
            err = CmdWriteCPUBlock(0x8B, oBlock);
        }else{   
            err = CmdWriteCPUBlock(0x88, oBlock);
        }
        if (err != nil) {
            return 506;
        }

        if(PosParam.CPU_B){
            err = CmdWriteCPUBlock(0x8A, oBlock);
        }else{   
            err = CmdWriteCPUBlock(0x87, oBlock);
        }
        if ( err != nil ){
            return 507;
        }
    }else{
        err = PcdWrite ( GetCardArea(PosParam.M1Purse.Sector, 0), oBlock);
        if ( err != nil ){
            return 506;
        }

        err = PcdWrite ( GetCardArea(PosParam.M1Purse.Sector, 2), oBlock);
        if ( err != nil ){
            return 507;
        }
    }

    if(PosParam.FreeUseMonFlag == 0) {//1??????
       Reader.Purse.DateUsage = oBalance;
    }else { 
       Reader.Purse.MonthUsage = oBalance2;
    }

    return 0;
}


uint16_t CheckM1Card ()
{
    return PcdRead(GetCardArea(PosParam.M1Purse.Sector, 1), (uint8_t *)&GNumBlockTem);
}

uint16_t CheckCpuCard()
{
    return CmdReadCPURand();
}

void CheckCardOn(void)  //????
{
    PcdReset();
    PcdAntennaOff();
    delay_ms (2);
    PcdAntennaOn();
    while (PcdRequest(PICC_REQALL, NULL) == MI_OK) {
        PcdReset();
        PcdAntennaOff();
        delay_ms(2);
        PcdAntennaOn();
        delay_ms(10);
    }
    PcdReset();
    PcdAntennaOff();
    delay_ms (2);
    PcdAntennaOn();
}

TPurse NewPurse()
{
    TPurse T;
    T.CardType = 0x0400;
    memset(T.EmbedSN, 0, 4);
    T.TradeSerial = 0;
    T.Balance = 0;
    return T;
}

TReader *NewReader()
{
    Reader.Find = FindCard;
    Reader.HaltCard = PcdHalt;

    Reader.ReadNum = ReadNum;
    Reader.ReadMoney = ReadMoney; 
    Reader.PrepaySmall = PrepaySmall;
    Reader.FreePaySmall = FreePaySmall;
//    Reader.WaitOver = WaitOver;
    Reader.Purse = NewPurse();
    memset(CPUReadBuf, 0, 64);

    return (TReader *)&Reader;
}